#pragma once

#include "bdpp/core.h"

namespace bd {
constexpr char VERSION[] = "0.0.1";
struct StaticCheck {
  static_assert(sizeof(int32_t) == 4, "error: type 'int32_t' has unsupported size");
  static_assert(sizeof(uint64_t) == 8, "error: type 'uint64_t' has unsupported size");
  static_assert(sizeof(double) == 8, "error: type 'double' has unsupported size");
};

// 文件路径长度
constexpr uint64_t FILE_NAME_LEN = 1U << 8U;
// 即时消息(错误信息)长度
constexpr uint64_t BUFFER_LEN = 1U << 9U;

template <class Iter>
using compare = std::function<bool(std::add_lvalue_reference_t<std::add_const_t<typename std::iterator_traits<Iter>::value_type>>,
                                   std::add_lvalue_reference_t<std::add_const_t<typename std::iterator_traits<Iter>::value_type>>)>;

// 异常
class Error : public std::exception {
 protected:
  mutable char buffer_[BUFFER_LEN];

 public:
  explicit Error(const std::string& msg) : buffer_{} {
    std::memset(buffer_, 0, sizeof buffer_);
    std::memcpy(buffer_, msg.c_str(), msg.size());
  }

  explicit Error(const char* msg) : buffer_{} { std::memcpy(buffer_, msg, sizeof buffer_); }

  auto what() const noexcept -> const char* override { return buffer_; }
};

// 异步FIFO队列
template <class T>
class Queue {
  mutable std::mutex m_;
  mutable std::condition_variable cv_push_, cv_pop_;
  std::list<T> l_;
  std::size_t max_len_;

 public:
  explicit Queue(std::size_t max_len = 0) noexcept : max_len_(max_len) {}

  auto size() const noexcept -> std::size_t {
    std::lock_guard lk(m_);
    return l_.size();
  }

  auto push_nowait(const T& e) -> bool {
    std::lock_guard lk(m_);
    if (max_len_ == 0 || l_.size() < max_len_) {
      l_.push_back(e);
      return true;
    }
    return false;
  }

  void push(const T& e) {
    std::unique_lock lk(m_);
    if (max_len_ != 0 && l_.size() >= max_len_) {
      cv_push_.wait(lk, [&] { return l_.size() < max_len_; });
    }
    l_.push_back(e);
    cv_pop_.notify_one();
  }

  auto pop() -> T {
    std::unique_lock lk(m_);
    if (l_.empty()) {
      cv_pop_.wait(lk, [&] { return !l_.empty(); });
    }
    auto ret = l_.front();
    l_.pop_front();
    cv_push_.notify_one();
    return ret;
  }
};
}  // namespace bd
